home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-09-12 | 14.6 KB | 192 lines | [TEXT/MPS ] |
-
-
- Leaks:
- a dcmd to help find memory leaks
-
- from Bo3b Johnson
- MS 37-DS
-
- 10/17/90
-
- Release Notes:
-
- What leaks are:
- Memory leaks are those problems in programs where a Handle or a Ptr are allocated but never
- disposed. This sort of problem is hard to track down, because programs typically allocate a
- ton o' blocks while they work. If a block is not disposed over a given operation, you usually build
- up more and more of these orphaned blocks until the program runs out of memory. 'Properly
- written Macintosh programs will run for hours, even days, without crashing.' To point out the
- reason why these are such good bugs to solve before shipping, consider what would happen if all
- the windows that were allocated in a program were never disposed...
-
- Historically, the way to spot leaks has been to do a heap dump and watch if free space keeps going
- down. This doesn't tell you where the leak is, but it lets you see the blocks that are repetitious, and
- it tells you that there is a leak somewhere.
-
- How to install:
- What I've done here is to make a tool to spot leaks, now in it's second incarnation as a dcmd.
- Previously it was seriously skanky by being built in three separate code resources. Only a few
- people had the pleasure of using version 1. This version is a happy dcmd, and is completely
- contained in the one dcmd resource. Paste it into your debugger prefs, reboot, and away you go.
-
- Basic operation:
- 1) Launch the application you want to examine. This sets up the long term storage that the app
- needs, and this tool can't help find leaks during a startup time.
- 2) Turn the watching code on by 'Leaks On'. It will cough out some info that is marginally helpful,
- like the maximum number of blocks it can watch at one time. This is hard coded right now to
- be 500 blocks.
- 3) Go, then exercise the program in question. In order to differentiate between a memory leak, and
- something that just hangs around for a long time, I need you to run the sequence in question three
- or more times. As an example, open and close a document three times.
- 4) Break into Macsbug, and do a 'Leaks Off'. This turns off the watching code, and has it analyze the
- info it collected to see if there is a series of blocks (3 or more) that look like they might be a leak. If
- something looks right, it will give you a minor stack crawl of how a block was allocated. The block
- address, and size are also displayed so you can check it out further. The 'matches' field tells you
- how many blocks match the one being displayed, in terms of size and stack crawl.
-
- Other options:
- It has your basic help for a dcmd, and has 4 options.
- Leaks On- Turns on the watching code, so it will track every NewHandle/NewPtr
- DisposHandle/DisposPtr that the system does, including the system heap, and all heaps
- anywhere. It also clears out the b-tree I use to watch for blocks, zeroing the whole tree.
- Leaks Off- Turns off the code, but leaves the b-tree intact. It will analyze the entire tree to
- see if there are several blocks that are the same size, allocated from the same piece of code.
- If there are 3 or more candidates, it is considered a leak and displayed.
- Leaks Dump- Dumps out every element in the b-tree, giving the address of the block being
- watched, the size, and the stack crawl for who allocated the block. This isn't terribly helpful,
- but can give you each block being tracked with no filtering.
- Leaks- If you just type the command with no option, it will leave the watching state as is, but
- will do the tree analysis looking for leaks, and displaying likely candidates. This is sort of a
- status mode, in case you aren't sure if something is leaking and don't want to change the
- watching state.
- In each case, it will also dump out the header information so that you can see the state of the
- watcher. This includes info like the number of memory blocks it is currently watching, and
- the watching state itself (either on or off). This also gives you that vital feedback to let you
- know that something happened in response to the command.
-
- Another way to use this tool would be to embed DebugStr commands in your program, surrounding
- an area that should be checked. You can do a 'Leaks On; g' at the start of some operation, and
- do a 'Leaks Dump' at the end. If you are sure that no memory should be left around over a
- given piece of code, then my tracking tree should be empty. If it isn't, then any block that is
- displayed would be a likely leak. Another way to use the tool would be to turn it on and
- exercise the application for a longer time, not making any effort to do operations three times.
- Presumably, if there are leaks, then the leaking section of code will get hit several times
- during normal operation, satisfying the three or more criteria. This can give you a larger
- number of potential leaks to examine, but if the tool identifies some candidates during that
- time, they are at least worth looking at.
-
- Ways to fake me out:
- -If you turn on Leaks and then launch an app, you are sure to create a bunch of blocks, and quite
- possibly some of them will have the same size and stack crawl. I'll tag them as a likely leak, but
- it is probably fine. In addition, if you launch something like MPW, you will quite likely run out
- of records in the free list, causing me to DebugStr to let you know. It is unlikely that I can give
- you good information during a launch, so I recommend turning leaks on after the app is started.
- -If you turn on Leaks, launch an app, then quit, then launch again. When apps quit they typically
- don't clean up their blocks, and I don't catch the application leaving yet. When the app is
- relaunched, the same blocks will probably get allocated, frying my b-tree because it will have two
- records watching the same block. When the tool is turned on or off I check the tree for consistency
- and bitch about blocks or other bad things that may have happened to the tree. Reinitialize the
- tree by doing a Leak On, to clear the error state.
- -If you watch a bunch of blocks, in the 300 block range, it is entirely possible that I will overflow the
- Macsbug stack when displaying the info. Macsbug will let you know. This usually happens only
- when an app gets launched.
- -If a code block moves around a lot, I may not see a memory leak that I should. This is because the
- stack crawl could be different each time the block was allocated. This is sort of unlikely since
- most programs tend to be consistent, but it is possible.
-
- If you're using TMon Pro:
- You have to have the latest version of TMon Pro, the b2238 version doesn't work with this
- dcmd. I added a call to dcmdSwapWorlds so that I patch the traps in the mac world, instead of
- in the TMon world. It seems to work fine from that point. This is slightly more risky in TMon
- than in Macsbug, since I install tail patches in this case. For the macsbug case, the patches I make
- come in before system patches, so the tail patch problem is gone. Since TMon comes in at
- init time, after the system patches, I will patch traps that may disable a system bug fix. I don't
- believe there is any conflict on any machine today, but I haven't looked that hard.
-
- How it works:
- At Macsbug load time, before system patches, the dcmd gets called to do
- it's Init. The Init patches the four traps that are deemed handy: NewHandle/NewPtr/
- DiposHandle/DisposPtr, and allocates a big pointer in the system heap that is filled with records
- that track individual memory blocks. The block is about 28K in size, and is set up to watch
- 500 possible allocations. The block is cleared, and setup as a linked list of empty records, waiting
- to be added to the b-tree. The b-tree is the data structure that does all the work in terms of
- keeping track of things. When a block is allocated by anybody in the system, my trap patch
- gets called. That patch saves off the size of the block, the address that came back, and an 8
- level stack crawl to keep track of where a block was allocated. If the stack crawl cannot go
- back a full 8 levels, the remaining entries are zero. All this info is saved into one
- of these records in my pointer block, and the record is moved out of the empty list and into
- the b-tree list. When the record is added to the b-tree list, it is sorted into the tree using the
- address of the block being watched. I use a binary tree so that the watching code can run
- without slowing the machine down, like it would if I were using a linear list. All blocks that
- are actively being watched are in the b-tree as separate records. The rest of the 500 records
- are in the empty list, and are available. The heads of both lists are shown when you do any
- command. When a block is deallocated by a program, it will call something like DisposHandle,
- and the trap patch will catch that too. It sees the handle being disposed, and looks in the b-tree
- for a matching record. If it finds it, it will futz around with the b-tree to keep it nicely set up,
- but remove the record from the tree. It zeros the record, and adds it back into the free list.
- The records are thus reused all the time. Any block that was allocated but not yet disposed
- will be in the b-tree. That's about it. When you turn the watching tool
- off, or check its status, it will do the analysis of the tree. This involves driving the entire tree
- many times trying to find a series of blocks that have the same size, and were allocated by the
- same sequence of code (the stack crawl is the same). This is presumed to indicate a leak, since
- I don't expect a given sequence of code to keep allocating blocks without throwing them away.
- The analysis routine requires that there be 3 or more of the blocks in the b-tree that have the
- same size and stack crawl. It will dump an example candidate if it finds one. If no blocks
- qualify, then it won't display anything (but you see the header). There may be tons of blocks
- that it is actively watching, but few of them are tagged as likely to be leaks. If you run it for
- long enough, you may create more than 500 blocks that I would be watching. If I run out of
- records, I'll DebugStr to let you know. Whenever the Leaks code is called, I do a treewalk
- to be sure the b-tree is in a valid state. This involves making sure that there aren't two
- records in the tree with the same address (impossible for two blocks to be allocated with
- the same address), and that I have the 500 blocks when both lists are taken into account.
- If something went wrong, I'll let you know by writing the information to Macsbug.
-
-
- Theory of operation:
- Trying to find memory leaks is a problem that cannot be solved in a completely deteriministic
- way. The Macintosh makes it difficult to track memory usage, and provides no support for
- finding if a block is in use or not. Given the constraints, I chose to make the tool work in a
- heuristic way: I ask the user to do a given operation three or more times. If a given operation is
- leaking memory, then each time it is activated, you should get another block or two on the
- heap. Doing the operation three times makes it possible to differentiate between blocks that
- are persistent (allocated once and left around), and blocks that are being lost. An example of
- persistent data would be the CODE 1 resource in most applications. It is allocated once at the
- beginning of a program and not disposed until the app quits. There may be unrecognizable
- data that the app creates at startup time as well, something not related to resources. Blocks of
- this form are hard to differentiate from blocks allocated later. By doing the operation three
- times, I can look for things that are repetitive, and thus differentiate things that are showing
- up as data that is not being disposed. This approach means that I cannot find leaks that only
- happen once, like blocks leaked during an application startup. I am not particularly concerned
- about those leaks, since they are 'only' wasting memory. Those type of leaks won't cause the
- system to crash, as a repetitive leak will. The analysis code merely looks through the saved
- information to find three or more blocks that have the same size, and were allocated from
- the same sequence of code. This critieria can obviously fail in some circumstances, since
- there may be a sequence of code that allocates a number of blocks that look alike. An example
- might be a spot in the program where it loads in a number of resources. Since a series of
- GetResource calls will allocate memory, you could get a false reading that there is a leak. I've
- solved that specific example by ensuring that the blocks that are possible leaks are not
- recognized by the resource manager before I use them as leak candidates. There are a number
- of similar situations where I cannot discern the difference. In practical terms, this means the
- leak display is showing potential leaks, not sure leaks. The human still has to look at the
- results. By doing this operation though, I have trimmed down the number of blocks to look
- at, and in a lot of cases, I trim the number to zero. You can see memory leaks by looking at a
- heap dump, but this isn't the most efficient way. This tool should make it relatively painless
- to find leaks. There is the other side to the problem of filtering the block information though,
- and that is the possibility of filtering out blocks that actually are leaks, and thus not
- reporting one that has been leaked. This is the certainly the case for one-time leaks, like
- during startup. Given a relatively discrete operation, you shouldn't run into this problem.
- The only way I can currently see it happening is if a block is allocated from a code segment
- that keeps moving around in memory. Most applications don't have code segments that
- are actually in a different location every time they allocate memory; but it is possible.
-
-
- As always, if you find bugs in this software, let me know. In fact, tell me what you think.
- If you hate it, or like it, I would be interested in knowing. There are a number of tools of
- this form that I could make, so feedback is helpful. I made this one because I wanted an
- easier way to find memory leaks. If you find it useful, let me know. If you have ideas
- for improvement, those are welcome too.
- Bo3b.
-
-
-
-